
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *                                                                   *
 * Procés encarregat de la simulació de les lectures del vent i la   *
 * recollida dels angles de consigna de la pràctica de SITR.         *
 *                                                                   *
 * Per compilar: gcc simulador.c -o simulador -lrt -lm               *
 *                                                                   *
 * Curs: 2010 - 2011                                                 *
 * Professora Coordinadora: M.Àngels Moncusí                         *
 * Professor pràctiques: Gerard Sanromà                              *
 *                                                                   *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>
#include <semaphore.h>
#include <errno.h>
#include <error.h>
#include <math.h>

#include "specs.h"

#define FITXER_VENT "lectures_vent.dat"
#define SIGMA (5.0) /* escala dels errors */

/* Variables globals */
static int no_final,errors;
FILE *f_vent;
static float vent_actual;
mqd_t idc_vent1, idc_vent2, idc_orient;
struct mq_attr attr;
sem_t sem_fi, sem_temps;
char *buf_vent1, *buf_vent2, *buf_orient;
long mida_cua_vent1, mida_cua_vent2, mida_cua_orient;
struct sigevent sev2, sev3;
int iseed; 
int temps;

/* Taules id procs amb les seves orientacions actuals */
char id_procs[MAX_PROCS][MAX_ID_PROC];
float orient_procs[MAX_PROCS];
int procs_act;

/* Taules d'errors d'orientacio comesos */
float err_moli[MAX_PROCS][MAX_TEMPS];

/* Taula de valors de vent reals */
float val_vent[MAX_TEMPS];

void grnf (x,y)
/* Two Gaussian random numbers generated from two uniform
   random numbers.  Copyright (c) Tao Pang 1997. */
double *x,*y;
{
double pi,r1,r2;
double ranf();

pi =  4*atan(1);
r1 = -log(1-ranf());
r2 =  2*pi*ranf();
r1 =  sqrt(2*r1);
*x  = r1*cos(r2);
*y  = r1*sin(r2);
}

double ranf()
/* Uniform random number generator x(n+1)= a*x(n) mod c
   with a = pow(7,5) and c = pow(2,31)-1.
   Copyright (c) Tao Pang 1997. */
{
const int ia=16807,ic=2147483647,iq=127773,ir=2836;
int il,ih,it;
double rc;
extern int iseed;
ih = iseed/iq;
il = iseed%iq;
it = ia*il-ir*ih;
if (it > 0)
  {
  iseed = it;
  }
else
  {
iseed = ic+it;
  }
rc = ic;
return iseed/rc;
}

void surt_amb_error(void) {
	
	no_final = 0;
	errors = 1;
	sem_post(&sem_fi);
}

/* Aquesta funció va variant l'angle global del vent en el temps */
void canvi_vent(sigval_t info)
{
	char s[512];
	
	if (no_final == 0) return;
	
	if (fgets(s,512,f_vent)==NULL) {
		printf("(canvi_vent) Hem arribat al final\n");
		no_final = 0;
		sem_post(&sem_fi);
	}
	if (sscanf(s,"%e\n",&vent_actual) == EOF) {
		printf("(canvi_vent) Error de conversió del fitxer de vent\n");
		surt_amb_error();
	}
	printf("(canvi_vent) he llegit %e\n",vent_actual);

	/* Acces a variable temps protegit per semafor */
	if (sem_wait(&sem_temps) == -1) {
		printf("Error bloquejant-me al semafor del temps\n");
		surt_amb_error();
	}
	temps++;
	val_vent[temps] = vent_actual;
	if (sem_post(&sem_temps) == -1) {
		printf("Error desbloquejant el semafor del temps\n");
		surt_amb_error();
	}
	
	if (temps >= MAX_TEMPS) {
		printf("(canvi_vent) Hem arribat al temps maxim\n");
		no_final = 0;
		sem_post(&sem_fi);
	}
}

/* Aquesta funció proporciona les lectures del vent als molins */
void lectura_vent(sigval_t info)
{
	int i,hi_es;
	float orient_moli;
	double x,y;
	float angle;
	int mida;
	
	if (no_final == 0) return;
	
	/* Registrem tasca lectura_vent a propera recepcio */
    if (mq_notify(idc_vent1,&sev2) == -1) {
		printf("Error en el registre tasca lectura vent a la cua\n");
		surt_amb_error();
	}
	
	struct mq_attr attr;
	mq_getattr(idc_vent1,&attr);
	while(attr.mq_curmsgs>0)
	{
		/* Llegeixo de la cua el id proces */
		if (mq_receive(idc_vent1,buf_vent1,mida_cua_vent1,NULL)==-1) {
			printf("(lectura_vent) Error llegint id proces de cua vent\n");
			surt_amb_error();
		}
		printf("(lectura_vent) he llegit el id %s\n",buf_vent1);

		/* Comprovo si esta a la llista i si no l'afegeixo */
		i = 0;
		hi_es = 0;
		while (hi_es == 0 && i < procs_act) {
			if (strcmp(buf_vent1,id_procs[i])==0) {
				hi_es = 1;
			} else {
				i++;
			}
		}
		if (hi_es == 0) { /* si no hi era l'afegeixo */
			strcpy(id_procs[procs_act],buf_vent1);
			orient_procs[procs_act] = 0.0;
			procs_act++;
			printf("(lectura_vent) He afegit el proces %s\n",buf_vent1);
		}

		/* Escric la orientacio relativa al moli amb error */
		x = (double)rand();
		y = (double)rand();
		grnf(&x,&y);
		angle = (float) (vent_actual - orient_procs[i] + ((float)(x * SIGMA)));
		if ((mida = sprintf(buf_vent2,"%f",angle)) < 0) {
			printf("(lectura_vent) Error convertint lectura a string\n");
			surt_amb_error();
		}
		if (mq_send(idc_vent2,buf_vent2,mida,1)==-1) {
			printf("(lectura_vent) Error enviant lectura a la cua\n");
			surt_amb_error();
		}
		mq_getattr(idc_vent1,&attr);
	}
}

/* Aquesta funció obté els angles consigna dels molins */
void escr_orient(sigval_t info)
{
	int i,j,hi_es;
	float consigna;
	char id_moli[MAX_ID_PROC];
	
	if (no_final == 0) return;
	
	/* Registrem tasca lectura_vent a propera recepcio */
    if (mq_notify(idc_orient,&sev3) == -1) {
		printf("Error en el registre tasca escr orient a la cua\n");
		surt_amb_error();
	}

	struct mq_attr attr;
	mq_getattr(idc_orient,&attr);
	while(attr.mq_curmsgs>0)
	{
		/* Llegeixo de la cua peticio orientacio */
		if (mq_receive(idc_orient,buf_orient,mida_cua_orient,NULL)==-1) {
			printf("(escr_orient) Error llegint orientacio de moli\n");
			surt_amb_error();
		}
		printf("(escr_orient) he llegit orientacio %s\n",buf_orient);

		/* Parsejo peticio */
		if (sscanf(buf_orient,"%s %f",id_moli,&consigna)==EOF) {
			printf("(escr_orient) Error parsejant entrada consigna\n");
			surt_amb_error();
		}
		/*printf("(escr_orient) He parsejat consigna %s %f\n",id_moli,consigna);	*/

		/* Comprovo si esta a la llista */
		i = 0;
		hi_es = 0;
		while (hi_es == 0 && i < procs_act) {
			if (strcmp(id_moli,id_procs[i])==0) {
				hi_es = 1;
			} else {
				i++;
			}
		}
		if (hi_es == 0) { /* si no hi es ERROR */
			printf("(escr_orient) El moli que m'ha enviat la consigna no existeix\n");
			surt_amb_error();
		}
	
		/* Actualitzo angle del moli amb la consigna */
		orient_procs[i] += consigna;


		/* Actualitzo l'error comes pel moli */

		/* Acces a variable temps protegit per semafor */
		if (sem_wait(&sem_temps) == -1) {
			printf("Error bloquejant-me al semafor del temps\n");
			surt_amb_error();
		}

		j = temps;

		if (sem_post(&sem_temps) == -1) {
			printf("Error desbloquejant el semafor del temps\n");
			surt_amb_error();
		}
		
		while (err_moli[i][j] == -1.0 && j>=0) {
			err_moli[i][j] = fabs(val_vent[j] - orient_procs[i]);
			j--;
		}
		mq_getattr(idc_orient,&attr);
	}
}

int main(void)
{
    struct sigaction act;
    timer_t crono;
    struct sigevent sev;
    struct itimerspec its;
	char s[512];
	int i,j;
	time_t seg;
	long nseg;
	float period;
	
    /* Llavor generador aleatoris */
    iseed = 4;

    /* Indicador final del proces */
    no_final = 1;
    
    /* Indicador finalitzacio amb exit */
    errors = 0;
    
	/* Inicialitzem contador processos moli a 0 */
	procs_act = 0;
	
	/* Inicialitzem el contador del temps */
	temps = 0;
	
	/* Inicialitzem semafor de bloqueig del main */
	if (sem_init(&sem_fi,0,0) == -1) {
		printf("Error en la inicialitzacio del semafor de bloqueig main\n");
		return(-1);
	}
	
	/* Inicialitzem semafor d'acces a la variable temps */
	if (sem_init(&sem_temps,0,1) == -1) {
		printf("Error en la inicialitzacio del semafor d'acces a temps\n");
		return(-1);
	}
	
	/* Fixem errors inicials a -1 */
	for (i=0;i<MAX_PROCS;i++)
		for (j=0;j<MAX_TEMPS;j++)
			err_moli[i][j] = -1.0;
	
    /* Obrim fitxer valors del vent i llegim primer valor */
    if ((f_vent = fopen(FITXER_VENT,"r"))==NULL) {
		printf("Error en l'obertura del fitxer del vent\n");
		return(-1);
	}
	if (fgets(s,512,f_vent)==NULL) {
		printf("Error llegint 1r valor de fitxer del vent\n");
		return(-1);
	}
	if (sscanf(s,"%e\n",&vent_actual) == EOF) {
		printf("Error de conversió del 1r valor del fitxer de vent\n");
		return(-1);
	}
	val_vent[temps] = vent_actual;
    
	/* Creem les cues */
	{
		mq_unlink(CUA_LECT_VENT1);
		mq_unlink(CUA_LECT_VENT2);
		mq_unlink(CUA_ESCR_ORIENT);
		/* Cua vent 1 */
		if ((idc_vent1 = mq_open(CUA_LECT_VENT1,O_RDONLY|O_CREAT,S_IRUSR|S_IWUSR,NULL)) == -1) {
			printf("Error en la creació cua vent 1\n");
			return(-1);
		}
		if (mq_getattr(idc_vent1,&attr)==-1) {
			printf("Error obtenint els atributs de la cua\n");
			return(-1);
		}
		mida_cua_vent1 = attr.mq_msgsize;
		buf_vent1 = (char *)malloc(mida_cua_vent1*sizeof(char));
		/* Cua vent 2 */
		if ((idc_vent2 = mq_open(CUA_LECT_VENT2,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR,NULL)) == -1) {
			printf("Error en la creació cua vent 2\n");
			return(-1);
		}
		if (mq_getattr(idc_vent2,&attr)==-1) {
			printf("Error obtenint els atributs de la cua\n");
			return(-1);
		}
		mida_cua_vent2 = attr.mq_msgsize;
		buf_vent2 = (char *)malloc(mida_cua_vent2*sizeof(char));
		/* Cua orient */
		if ((idc_orient = mq_open(CUA_ESCR_ORIENT,O_RDONLY|O_CREAT,S_IRUSR|S_IWUSR,NULL)) == -1) {
			printf("Error en la creació cua orientació\n");
			return(-1);
		}
		if (mq_getattr(idc_orient,&attr)==-1) {
			printf("Error obtenint els atributs de la cua\n");
			return(-1);
		}
		mida_cua_orient = attr.mq_msgsize;
		buf_orient = (char *)malloc(mida_cua_orient*sizeof(char));
	}

	/* Registrem tasca lectura_vent a recepcio de missatges de CUA_LECT_VENT */
    sev2.sigev_notify = SIGEV_THREAD;
    sev2.sigev_notify_function = lectura_vent;
    sev2.sigev_notify_attributes = NULL;
    if (mq_notify(idc_vent1,&sev2) == -1) {
		printf("Error en el registre tasca lectura vent a la cua\n");
		return(-1);
	}
	
	/* Registrem tasca escr_orient a recepcio de missatges de CUA_ESCR_ORIENT */
    sev3.sigev_notify = SIGEV_THREAD;
    sev3.sigev_notify_function = escr_orient;
    sev3.sigev_notify_attributes = NULL;
    if (mq_notify(idc_orient,&sev3) == -1) {
		printf("Error en el registre tasca lectura vent a la cua\n");
		return(-1);
	}
	
	/* Activem tasca periòdica d'actualitzacio dels valors del vent */
    sev.sigev_notify = SIGEV_THREAD;
    sev.sigev_notify_function = canvi_vent;
    sev.sigev_notify_attributes = NULL;
    sev.sigev_value.sival_ptr = &crono;
	if (timer_create(CLOCK_REALTIME,&sev,&crono) == -1) {
		printf("Error en la creació del timer\n");
		return(-1);
	}
	/* Calculem valor del interval usant el valor de FREQ_VENT */
	period = (float)(1.0/FREQ_VENT);
	seg = (time_t)floor(period);
	nseg = (long)((period - ((float)seg))*CONST_NSEG);
	its.it_interval.tv_sec = seg;
	its.it_interval.tv_nsec = nseg;
	/* Temps per a la primera execucio */
	its.it_value.tv_sec = seg;
	its.it_value.tv_nsec = nseg;
	if (timer_settime(crono,0,&its,NULL) == -1)	{
		printf("Error en la iniciació del timer\n");
		return(-1);
	}

	/* Ens bloquejem fins que no acabi tot el proces */
	if (sem_wait(&sem_fi) == -1) {
		printf("Error bloquejant-me al semafor\n");
		return(-1);
	}
	
	if (errors == 1) {
		printf("\nLA EXECUCIÓ NO HA ACABAT CORRECTAMENT\n");
	}
	
	fflush(stdout);
	printf("\nHistòric d'errors en l'angle de cada molí:\n\n");
	for (i=0;i<procs_act;i++) {
		printf("moli %s:\n",id_procs[i]);
		for (j=0;j<temps;j++) {
			printf("%f ",err_moli[i][j]);
		}
		printf("\n-------------------------------------------\n");
	}			
	
	free(buf_vent1);
	free(buf_vent2);
	free(buf_orient);

	return(0);
}

